; I2C (TWI) master interface. 
; This is part of the Z80-CP/M emulator written by Sprite_tm.
;
;    Copyright (C) 2013 Leo C.
;
;    This file is part of avrcpm.
;
;    avrcpm is free software: you can redistribute it and/or modify it
;    under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;    (at your option) any later version.
;
;    avrcpm is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with avrcpm.  If not, see <http://www.gnu.org/licenses/>.
;
;    $Id$
;

#if I2C_SUPPORT 

/* General TWI Master status codes */
#define TWI_START                  0x08  /* START has been transmitted */
#define TWI_REP_START              0x10  /* Repeated START has been transmitted */
#define TWI_ARB_LOST               0x38  /* Arbitration lost */

/* TWI Master Transmitter status codes */
#define TWI_MTX_ADR_ACK            0x18  /* SLA+W has been transmitted and ACK received */
#define TWI_MTX_ADR_NACK           0x20  /* SLA+W has been transmitted and NACK received */
#define TWI_MTX_DATA_ACK           0x28  /* Data byte has been transmitted and ACK received */
#define TWI_MTX_DATA_NACK          0x30  /* Data byte has been transmitted and NACK received */

/* TWI Master Receiver status codes */
#define TWI_MRX_ADR_ACK            0x40  /* SLA+R has been transmitted and ACK received */
#define TWI_MRX_ADR_NACK           0x48  /* SLA+R has been transmitted and NACK received */
#define TWI_MRX_DATA_ACK           0x50  /* Data byte has been received and ACK transmitted */
#define TWI_MRX_DATA_NACK          0x58  /* Data byte has been received and NACK transmitted */

/* TWI Miscellaneous status codes */
#define TWI_NO_STATE               0xF8  /* No relevant state information available */
#define TWI_BUS_ERROR              0x00  /* Bus error due to an illegal START or STOP condition */


#define I2C_BR ((F_CPU / (2 * I2C_CLOCK)) - 8) /* I2C Bit Rate */

;----------------------------------------------------------------------
;
; TWINT: TWI Interrupt Flag
; TWEA:  TWI Enable Acknowledge Bit
; TWSTA: TWI START Condition Bit
; TWSTO: TWI STOP Condition Bit
; TWEN:  TWI Enable Bit
; TWIE:  TWI Interrupt Enable
; 
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|           (1<<TWEA)
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)
; 
; default:
; 	(1<<TWEN)|          (1<<TWINT)|           (1<<TWSTO)
; 
; Init:
; 	(1<<TWEN)
; 
; start read/write:
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
; 	(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
; 
; wait ready:
; 	 (1<<TWIE)|(1<<TWSTO)
;
;----------------------------------------------------------------------
;
;i2c_result	<  0	Error. 
;			-1 general/unknown error
;			-2 time out 
;		>= 0	No error
;			> 0 status tbd.
;
;----------------------------------------------------------------------

	.dseg

i2c_var:
i2ci_idx:
	.byte	1
i2c_result:
	.byte	1
i2c_bufcnt:
	.byte	1
i2c_buf:
	.byte	I2C_BUFSIZE

	.equ oi2ci_idx   = 0
	.equ oi2c_result = 1
	.equ oi2c_bufcnt = 2
	.equ oi2c_buf    = 3

;------------------------------------------------------------------

	.cseg


	INTERRUPT TWIaddr
	
	push	temp
	in	temp,sreg
	push	temp
	push	temp2
	push	zh
	push	zl

	ldiw	z,i2c_var
	ldd	temp2,z+oi2ci_idx			

	inm8	temp,TWSR
	cpi	temp,TWI_START
	breq	i2ci_START
	cpi	temp,TWI_REP_START
	breq	i2ci_REP_START
	cpi	temp,TWI_MTX_ADR_ACK
	breq	i2ci_MTX_ADR_ACK
	cpi	temp,TWI_MTX_DATA_ACK
	breq	i2ci_MTX_DATA_ACK
	cpi	temp,TWI_MRX_ADR_ACK
	breq	i2ci_MRX_ADR_ACK
	cpi	temp,TWI_MRX_DATA_ACK
	breq	i2ci_MRX_DATA_ACK
	cpi	temp,TWI_MRX_DATA_NACK
	breq	i2ci_MRX_DATA_NACK
	rjmp	i2ci_default

i2ci_START:
i2ci_REP_START:
	clr	temp2					;reset buffer pointer
i2ci_MTX_ADR_ACK:
i2ci_MTX_DATA_ACK:
	ldd	temp,z+oi2c_bufcnt
	cp	temp2,temp				;all bytes tranmited?
	brsh	i2ci_12					; yes
	add	zl,temp2
	adc	zh,_0
	inc	temp2
	ldd	temp,z+oi2c_buf				;next byte
	outm8	TWDR,temp
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)
	rjmp	i2ci_end

i2ci_12:
	std	z+oi2c_result,_0			;done
	rjmp	i2ci_default				;stop transceiver

i2ci_MRX_DATA_ACK:
	add	zl,temp2
	adc	zh,_0
	inc	temp2
	inm8	temp,TWDR
	std	z+oi2c_buf,temp
i2ci_MRX_ADR_ACK:
	lds	temp,i2c_bufcnt
	dec	temp
	cp	temp2,temp
	brsh	i2ci_32
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)
	rjmp	i2ci_end
i2ci_32:
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)
	rjmp	i2ci_end

i2ci_MRX_DATA_NACK:
	std	z+oi2c_result,_0			;result = ok
	add	zl,temp2
	adc	zh,_0
	inm8	temp,TWDR
	std	z+oi2c_buf,temp
;	fall thru

i2ci_default:
	ldi	temp,(1<<TWEN)|(1<<TWINT)|(1<<TWSTO)

i2ci_end: 
	outm8	TWCR,temp
	sts	i2ci_idx,temp2
	pop	zl
	pop	zh
	pop	temp2
	pop	temp
	out	sreg,temp
	pop	temp
	reti

;------------------------------------------------------------------

i2c_init:
	ldi	temp,I2C_BR
	outm8	TWBR,temp
	outm8	TWDR,_255			;
	ldi	temp,(1<<TWEN)			;Enable TWI, disable TWI interrupt.
	outm8	TWCR,temp

	sts	i2c_result,_0
	ret

;------------------------------------------------------------------

i2c_waitready:

	ldi	temp,30
	sts	delay_timer1,temp
i2c_wrl:
	inm8	temp,TWCR
	andi	temp,(1<<TWIE)|(1<<TWSTO)
	breq	i2c_wre
	lds	temp,delay_timer1
	tst	temp
	brne	i2c_wrl

	ldi	temp,-2
	sts	i2c_result,temp
i2c_wre:
	tst	temp
	ret

;------------------------------------------------------------------
;
; 	z:	Pointer to the data to write.
;		First byte is slave address
;	temp2:	Number of bytes to write including address byte.
;

i2c_write:

	rcall	i2c_waitready
	brmi	i2c_we
	push	zh
	push	zl
	push	xh
	push	xl

	ldiw	x,i2c_result
	st	x+,_255				;result = not ok
	st	x+,temp2			;store size
	ld	temp,z+
	cbr	temp,0x01
i2c_wl:
	st	x+,temp
	dec	temp2
	breq	i2c_wle
	ld	temp,z+
	rjmp	i2c_wl
i2c_wle:
	; Enable TWI, TWI int and initiate start condition
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
	outm8	TWCR,temp

	pop	xl
	pop	xh
	pop	zl
	pop	zh
i2c_we:
	ret


;------------------------------------------------------------------
;
; 	z:	Pointer to data buffer.
;		First byte of buffer is slave address
;	temp2:	Number of bytes to write including address byte.
;
;	temp:	return (fail = 0, else succsess)

i2c_read:

	rcall	i2c_waitready
	brmi	i2c_re

	push	zh
	push	zl
	push	xh
	push	xl
	ldiw	x,i2c_result
	st	x+,_255				;result = not ok
	st	x+,temp2			;store size
	ld	temp,z
	sbr	temp,0x01
	st	x,temp

	; Enable TWI, TWI int and initiate start condition
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
	outm8	TWCR,temp

	rcall	i2c_waitready

	lds	temp,i2c_result
	sbrc	temp,7
	rjmp	i2c_ex				;
i2c_rl:
	ld	temp,x+
	st	z+,temp
	dec	temp2
	brne	i2c_rl

i2c_ex:
	pop	xl
	pop	xh
	pop	zl
	pop	zh
i2c_re:
	lds	temp,i2c_result
	ret

;------------------------------------------------------------------

	.dseg

vi2c_stat:
	.byte	1
vi2c_blen:
	.byte	1
vi2c_addr:
	.byte	2

	.cseg

vi2c_stat_get:
	lds	temp,i2c_result
	ret

vi2c_param_get:
	ldiw	z,vi2c_blen
	add	zl,temp3
	adc	zh,_0
	ld	temp,z
	ret

vi2c_param_set:
	ldiw	z,vi2c_blen
	add	zl,temp3
	adc	zh,_0
	st	z,temp
	ret

;------------------------------------------------------------------
;
; 	x:	Pointer to the data buffer.
;		First byte is slave address
;	temp3:	Number of bytes to write including address byte.
;

vi2c_read:

	rcall	i2c_waitready
	brmi	vi2c_rex

	ldiw	z,i2c_var
	std	z+oi2c_result,_255		;result = not ok
	lds	temp3,vi2c_blen
	std	z+oi2c_bufcnt,temp3		;store size
	adiw	z,oi2c_buf
	ldsw	x,vi2c_addr
	lcall	dram_read_pp
	sbr	temp,0x01
	st	z+,temp
	dec	temp3

	; Enable TWI, TWI int and initiate start condition
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
	outm8	TWCR,temp

	rcall	i2c_waitready

	lds	temp,i2c_result
	sbrc	temp,7
	rjmp	vi2c_rex				;
vi2c_rl:
	ld	temp,z+
	lcall	dram_write_pp
	dec	temp3
	brne	vi2c_rl

vi2c_rex:
	ret

;------------------------------------------------------------------
;
; 	x:	Pointer to the data to write.
;		First byte is slave address
;	temp2:	Number of bytes to write including address byte.
;

vi2c_write:

	rcall	i2c_waitready
	brmi	vi2c_wex
	ldiw	z,i2c_var
	std	z+oi2c_result,_255		;result = not ok
	lds	temp3,vi2c_blen
	std	z+oi2c_bufcnt,temp3		;store size
	adiw	z,oi2c_buf
	ldsw	x,vi2c_addr
	lcall	dram_read_pp
	cbr	temp,0x01
vi2c_wl:
	st	z+,temp
	dec	temp3
	breq	vi2c_wle
	lcall	dram_read_pp
	rjmp	vi2c_wl
vi2c_wle:
	; Enable TWI, TWI int and initiate start condition
	ldi	temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
	outm8	TWCR,temp
vi2c_wex:
	ret

;------------------------------------------------------------------


vi2c_ctrl:
	cpi	temp,1
	brne	vi2c_c1
	rjmp	vi2c_read

vi2c_c1:
	cpi	temp,2
	brne	vi2c_c2
	rjmp	vi2c_write
vi2c_c2:
vi2c_ce:
	ret

;------------------------------------------------------------------


pcf8574_in:
	; make a buffer on stack
	push	_255			;place holder for input value
	in	zh,sph
	in	zl,spl
	ldi	temp,0x20		;pcf8574 address (7 bit)
	add	temp,temp3
	lsl	temp
	push	temp			;slave address
	ldi	temp2,2
	rcall	i2c_read
	pop	temp			;remove slave address from stack
	pop	temp			;return input value
	ret

pcf8574_out:
	; make a buffer on stack
	push	temp			;output value
	in	zh,sph
	in	zl,spl
	ldi	temp,0x20		;pcf8574 address (7 bit)
	add	temp,temp3
	lsl	temp
	push	temp			;slave address
	ldi	temp2,2
	rcall	i2c_write
	pop	temp			;remove buffer from stack
	pop	temp			;
	ret


#endif /* I2C_SUPPORT */
;------------------------------------------------------------------
; vim:set ts=8 noet nowrap

